package com.ihr360.payroll.controller.payrollconfig;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.ihr360.authority.sharing.annonation.RestControl;
import com.ihr360.authority.sharing.privilege.AccessControlMethod;
import com.ihr360.authority.sharing.privilege.SearchPrivilege;
import com.ihr360.authority.sharing.util.AccessControlUtils;
import com.ihr360.commons.entity.AuthUserInfo;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.request.Ihr360SearchBody;
import com.ihr360.commons.utils.RestToReturnUtils;
import com.ihr360.maven.plugin.ModuleCodeStore;
import com.ihr360.maven.plugin.annotation.FunctionDefinition;
import com.ihr360.maven.plugin.annotation.ModuleDefinition;
import com.ihr360.payroll.config.PayrollConfigEntity;
import com.ihr360.payroll.model.dto.salary.meta.PayrollConfig;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.service.payroll.PayrollConfigService;
import com.ihr360.rest.service.SimpleRestService;
import com.ihr360.rest.webmvc.AbstractRestSimpleWithLongIdController;
import com.ihr360.validation.ValidationUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 薪资配置
 *
 * @author tomtao
 */

@Api(value = "薪资任务相关接口")
@RestController
@RequestMapping("/payrollConfigs")
@ModuleDefinition(mainModuleCode = ModuleCodeStore.Module.SALARY_CODE,
        mainModuleName = ModuleCodeStore.Module.SALARY_NAME,
        subModuleCode = ModuleCodeStore.Module.Salary.SALARY_SETTING_CODE,
        subModuleName = ModuleCodeStore.Module.Salary.SALARY_SETTING_NAME,
        hasExtendsAbstractRestController = true)
@RestControl(controls = {
        AccessControlMethod.METHODS.batchDelete,
        AccessControlMethod.METHODS.deleteOne,
        AccessControlMethod.METHODS.create,
        AccessControlMethod.METHODS.getAll,
        AccessControlMethod.METHODS.getAllInCompany,
        AccessControlMethod.METHODS.update})
public class PayrollConfigController extends AbstractRestSimpleWithLongIdController<PayrollConfig> {

    private static final Logger logger = LoggerFactory.getLogger(PayrollConfigController.class);

    @Autowired
    private PayrollConfigService payrollConfigService;

    @Override
    public SimpleRestService<PayrollConfig, Long> getSimpleRestService() {
        return payrollConfigService;
    }

    @FunctionDefinition(functionName = "重置配置为系统配置", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "重置配置为系统配置")
    @GetMapping(value = "/config/reset/{id}")
    public PayrollConfigEntity resetConfig(@PathVariable(name = "id") Long configId) {
        return payrollConfigService.resetPayrollConfigAsSystem(configId);
    }

    @FunctionDefinition(functionName = "获取一个工资表配置", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "获取一个工资表配置")
    @GetMapping(value = "/config/{id}")
    public PayrollConfigEntity getOne(@PathVariable(name = "id") Long configId) {
        return payrollConfigService.getOneEntity(configId);
    }

    @FunctionDefinition(functionName = "获取工资表list", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "获取工资表list")
    @GetMapping(value = "/list")
    public List<PayrollConfig> list() {
        return payrollConfigService.list();
    }

    @FunctionDefinition(functionName = "获取一个工资表的可编辑信息", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "获取一个工资表的可编辑信息")
    @GetMapping(value = "/edit/{id}")
    public PayrollConfig edit(@PathVariable(name = "id") Long configId) {
        return payrollConfigService.edit(configId);
    }

    @FunctionDefinition(functionName = "获取帐期内的方案提示", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @GetMapping(value = "/checkIsPayPeriod/{id}")
    public void checkIsPayPeriod(@PathVariable Long id, AuthUserInfo userInfo) {
        List<SalaryPlan> salaryPlanList = payrollConfigService.getIsPayPeriodSalaryPlanList(userInfo.getCompanyId(), id);
        if (!CollectionUtils.isEmpty(salaryPlanList)) {
            StringBuilder salaryPlanName = new StringBuilder();
            int listSize = salaryPlanList.size();
            int index = 1;
            for (SalaryPlan salaryPlan : salaryPlanList) {
                salaryPlanName.append(salaryPlan.getName()).append(index == listSize ? "" : ",");
                index++;
            }
            ValidationUtils.reject("0", "薪资表调整提示：　调整该薪资表，可能导致核算中的（" + salaryPlanName.toString() + "）方案结果不正确，确定要继续吗？");
        }
        RestToReturnUtils.setMessage("成功！");
    }

    @FunctionDefinition(functionName = "获取引用字段", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @GetMapping(value = "/getReferenceBaseFields/{id}")
    public ResponseEntity getReferenceBaseFields(@PathVariable Long id, @RequestParam String projection) {
        return toResource(payrollConfigService.getSalaryFieldByConfigIdAndTypeCategoryId(id, 1L), projection);
    }

    @FunctionDefinition(functionName = "是否被引用", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @GetMapping(value = "/isReferenced/{id}")
    public boolean isReferenced(@PathVariable Long id, @RequestParam boolean haveEnable) {
        return payrollConfigService.isReferenced(id, haveEnable);
    }

    @FunctionDefinition(functionName = "重置薪资字段公式", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "重置薪资字段公式")
    @PostMapping(value = "/reset/formula/{salaryFieldId}")
    public JSONObject resetFormulaToSystem(@PathVariable Long salaryFieldId) {
        return payrollConfigService.resetFormularToSystem(salaryFieldId);
    }

    @FunctionDefinition(functionName = "删除", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @DeleteMapping(value = "/delete/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public boolean deleteById(@PathVariable Long id) {
        if (payrollConfigService.isReferenced(id, false)) {
            ValidationUtils.reject("", "被引用不能删除！");
        }
        try {
            return payrollConfigService.deleteById(id);
        } catch (Ihr360Exception e) {
            throw e;
        } catch (DataIntegrityViolationException e) {
            logger.warn(e.getMessage(), e);
            throw new Ihr360Exception(null, "要删除的数据已被其他数据关联，无法删除");
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
            throw new Ihr360Exception(null, "无法删除数据，请刷新后重试");
        }
    }

    @FunctionDefinition(functionName = "获取一个薪资表", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "获取一个薪资表")
    @Override
    public ResponseEntity get(@PathVariable(name = "id") Long id,
                              @RequestParam(name = "projection", required = false) String projection) {
        PayrollConfig payrollConfig = payrollConfigService.getOneContainSystemTemplate(id);
        return toResource(payrollConfig, projection);
    }

    @FunctionDefinition(functionName = "保存薪资表", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "保存薪资表")
    @PutMapping(value = "/save")
    public void save(@RequestBody PayrollConfig entity, AuthUserInfo userInfo) {
        if (payrollConfigService.isDuplicateName(entity.getName(), entity.getId())) {
            ValidationUtils.reject("", "名称不能重复！");
        }
        entity.setCompanyId(userInfo.getCompanyId());
        payrollConfigService.saveOrUpdate(entity);
        RestToReturnUtils.setMessage("保存成功！");
    }

    @FunctionDefinition(functionName = "用户查询薪资表", functionCode = ModuleCodeStore.Function.PAYROLL_CONFIG)
    @ApiOperation(value = "用户查询薪资表")
    @Override
    public ResponseEntity executeSearch(@RequestBody Ihr360SearchBody<PayrollConfig> searchBody,
                                        @RequestParam(name = "projection", required = false) String projection) {
        super.setCompanyId(searchBody);
        SearchPrivilege searchPrivilege = new SearchPrivilege<>(
                super.getAuthUserResourceService(), super.getiAccessControlClass(), searchBody).doValidate();
        boolean isNoData = searchPrivilege.isNoData();
        List<Long> systemTemplateIdList = payrollConfigService.getSystemTemplateIdList();
        if (!CollectionUtils.isEmpty(systemTemplateIdList)) {
            isNoData = false;
        }
        Specification<PayrollConfig> specification = (root, cq, cb) -> {
            List<Predicate> predicates = Lists.newArrayList();
            Predicate predicate = searchBody.getSpecification().toPredicate(root, cq, cb);
            if (predicate != null) {
                predicates.add(predicate);
            }
            if (!CollectionUtils.isEmpty(systemTemplateIdList)) {
                CriteriaBuilder.In<Long> systemTemplateIdIn = cb.in(root.get(PayrollConfig.Field.ID).as(Long.class));
                systemTemplateIdList.forEach(systemTemplateIdIn::value);
                predicates.add(systemTemplateIdIn);
            }
            return cb.or((Predicate[]) predicates.toArray(new Predicate[predicates.size()]));
        };
        List<String> sortList = new ArrayList<>();
        String[] sort = searchBody.getSort();
        if (sort != null) {
            Collections.addAll(sortList, sort);
        } else {
            sortList.add(PayrollConfig.Field.IS_SYSTEM_TEMPLATE + ",DESC");
            sortList.add(PayrollConfig.Field.NAME + ",DESC");
        }
        searchBody.setSort(sortList.toArray(new String[sortList.size()]));
        Page<PayrollConfig> page = payrollConfigService.search(specification, searchBody.getPageable(), isNoData);

        List<PayrollConfig> content = page.getContent();
        Map<Long, Integer> privilegeMap = searchPrivilege.getUserResourcesPrivilege();
        Map<Long, Integer> publicityMap = searchPrivilege.getUserResourcesPublicity();
        content.forEach(s -> AccessControlUtils.setAccessControl2Target(s, privilegeMap, publicityMap));
        return toResource(page, projection);
    }

}